import java.io.IOException;
import java.io.ObjectInputStream;
import java.net.Socket;
import java.util.HashMap;
import java.util.HashSet;
import java.util.Queue;
import java.util.Map.Entry;
import java.util.Set;

public class Receiver extends Thread {
	private Socket s;
	private Queue<Message> receiveQueue;
	private HashMap<Rule, Integer> receiveRule;
	private Queue<Message> delayReceiveQueue;
	private MessagePasser mp;

	public Receiver(Socket s, Queue<Message> receiveQueue,
			HashMap<Rule, Integer> receiveRule,
			Queue<Message> delayReceiveQueue, MessagePasser mp) {
		this.s = s;
		this.receiveQueue = receiveQueue;
		this.receiveRule = receiveRule;
		this.delayReceiveQueue = delayReceiveQueue;
		this.mp = mp;
	}

	@Override
	public void run() {
		try {
			ObjectInputStream ois = new ObjectInputStream(s.getInputStream());
			Message m;
			while (true) {
				m = (Message) ois.readObject();
				// System.out.println(m);

				String action = checkRules(m, receiveRule);
				receiveWithRule(m, action);

				Set<Message> received = new HashSet<Message>(receiveQueue);

				mp.multicast(m.getData());
			}
		} catch (IOException e) {
			e.printStackTrace();
		} catch (ClassNotFoundException e) {
			e.printStackTrace();
		}

	}

	private void receiveWithRule(Message m, String action) {
		System.out.println("receive action: " + action);
		if (action == null) {
			receiveQueue.offer(m);
			if (!delayReceiveQueue.isEmpty()) {
				receiveQueue.offer(delayReceiveQueue.poll());
			}
		} else if (action.equals("drop")) {

		} else if (action.equals("duplicate")) {
			receiveQueue.offer(m);
			receiveQueue.offer(m);
			if (!delayReceiveQueue.isEmpty()) {
				receiveQueue.offer(delayReceiveQueue.poll());
			}
		} else if (action.equals("delay")) {
			delayReceiveQueue.offer(m);
		}
	}

	private String checkRules(Message m, HashMap<Rule, Integer> sendRules) {
		for (Entry<Rule, Integer> e : receiveRule.entrySet()) {
			Rule r = e.getKey();
			if (obeyRule(m, r)) {
				sendRules.put(r, sendRules.get(r) + 1);
				if (r.getNth() == -1
						|| (r.getNth() != -1 && r.getNth() == e.getValue()))
					return r.getAction();
			}
		}

		return null;
	}

	private boolean obeyRule(Message m, Rule r) {
		// System.out.println(r);
		if (r.getSrc() != null && !m.getSrc().equals(r.getSrc()))
			return false;
		if (r.getDest() != null && !m.getDest().equals(r.getDest()))
			return false;
		if (r.getKind() != null && !m.getKind().equals(r.getKind()))
			return false;
		if (r.getId() != -1 && m.getId() != r.getId())
			return false;

		return true;
	}

}
